cc.Class({
    extends: cc.Component,

    properties: {
        itemTemplate: { // item template to instantiate other items
            default: null,
            type: cc.Prefab
        },
        scrollView: {
            default: null,
            type: cc.ScrollView
        },
    },

    // use this for initialization
    onLoad: function () {
        this.content = this.scrollView.content;
        this.items = []; // array to store spawned items
        this.updateTimer = 0;
        this.updateInterval = 0.1;
        this.lastContentPosY = 0; // use this variable to detect if we are scrolling up or down
        this.lastContentPosX = 0; // use this variable to detect if we are scrolling up or down

        if (this.scrollView.vertical) {
            this.content.anchorX = 0.5;
            this.content.anchorY = 1;
        } else {
            this.content.anchorX = 0;
            this.content.anchorY = 0.5;
        }
        this.content.x = 0;
        this.content.y = 0;
    },

    /**
     * @param {*} totalCount // 列表总长度
     * @param {*} visibleCount // 显示区域的个数
     * @param {*} bufferCount // 缓冲区个数，一侧
     * @param {*} spacing // item之间的间隔
     * @param {*} updateCb // 回调
     */
    init(totalCount, visibleCount, bufferCount, spacing, updateCb) {
        this.totalCount = totalCount;
        this.visibleCount = visibleCount;
        this.bufferCount = bufferCount;
        this.spacing = spacing;
        this.updateCallback = updateCb;
        this._initialize();

        if (this.scrollView.vertical) {
            this.bufferZone = this.bufferCount * (this.items[0].height + this.spacing)
                + this.scrollView.node.height / 2 - this.items[0].height / 2;
        } else {
            this.bufferZone = this.bufferCount * (this.items[0].width + this.spacing)
                + this.scrollView.node.width / 2 - this.items[0].width / 2;
        }
        this.inited = true;
    },

    _initialize: function () {
        var tCnt = Math.min(this.totalCount, this.visibleCount + (this.bufferCount * 2));

        for (let i = 0; i < tCnt; ++i) { // spawn items, we only need to do this once
            let item = cc.instantiate(this.itemTemplate);
            item.itemID = i;
            this.content.addChild(item);
            if (this.scrollView.vertical) {
                item.setPosition(0, -item.height * (0.5 + i) - this.spacing * (i + 1));
            } else {
                item.setPosition(item.width * (0.5 + i) + this.spacing * (i + 1), 0);
            }
            this.items.push(item);
            this.updateCallback && this.updateCallback(item, i);
        }

        if (this.scrollView.vertical) {
            this.content.height = this.totalCount * (this.items[0].height + this.spacing) + this.spacing; // get total content height
        } else {
            this.content.width = this.totalCount * (this.items[0].width + this.spacing) + this.spacing; // get total content width
        }
    },

    _getPositionInView: function (item) { // get item position in scrollview's node space
        let worldPos = item.parent.convertToWorldSpaceAR(item.position);
        let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
        return viewPos;
    },

    _updateHorizental() {
        let items = this.items;
        let buffer = this.bufferZone;
        let isRight = this.scrollView.content.x > this.lastContentPosX; // scrolling direction
        let offset = (this.items[0].width + this.spacing) * items.length;
        for (let i = 0; i < items.length; ++i) {
            let viewPos = this._getPositionInView(items[i]);
            if (isRight) {
                // if away from buffer zone and not reaching top of content
                if (viewPos.x > buffer && items[i].x - offset > 0) {
                    items[i].x = items[i].x - offset;
                    let itemId = items[i].itemID - items.length; // update item id
                    items[i].itemID = itemId;
                    this.updateCallback && this.updateCallback(items[i], itemId);
                }
            } else {
                // if away from buffer zone and not reaching bottom of content
                if (viewPos.x < -buffer && items[i].x + offset < this.content.width) {
                    items[i].x = items[i].x + offset;
                    let itemId = items[i].itemID + items.length; // update item id
                    items[i].itemID = itemId;
                    this.updateCallback && this.updateCallback(items[i], itemId);
                }
            }
        }
        // update lastContentPosX
        this.lastContentPosX = this.scrollView.content.x;
    },
    _updateVertical() {
        let items = this.items;
        let buffer = this.bufferZone;
        let isDown = this.scrollView.content.y < this.lastContentPosY; // scrolling direction
        let offset = (this.items[0].height + this.spacing) * items.length;
        for (let i = 0; i < items.length; ++i) {
            let viewPos = this._getPositionInView(items[i]);
            if (isDown) {
                // if away from buffer zone and not reaching top of content
                if (viewPos.y < -buffer && items[i].y + offset < 0) {
                    items[i].y = items[i].y + offset;
                    let itemId = items[i].itemID - items.length; // update item id
                    items[i].itemID = itemId;
                    this.updateCallback && this.updateCallback(items[i], itemId);
                }
            } else {
                // if away from buffer zone and not reaching bottom of content
                if (viewPos.y > buffer && items[i].y - offset > -this.content.height) {
                    items[i].y = items[i].y - offset;
                    let itemId = items[i].itemID + items.length; // update item id
                    items[i].itemID = itemId;
                    this.updateCallback && this.updateCallback(items[i], itemId);
                }
            }
        }
        // update lastContentPosY
        this.lastContentPosY = this.scrollView.content.y;
    },

    update: function (dt) {
        if (!this.inited) {
            return;
        }
        this.updateTimer += dt;
        if (this.updateTimer < this.updateInterval) return; // we don't need to do the math every frame
        this.updateTimer = 0;
        if (this.scrollView.vertical) {
            this._updateVertical();
        } else {
            this._updateHorizental();
        }
    },
});

